package secret_api

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"

	"gitee.com/go-mao/mao/libs/binding"
	"gitee.com/go-mao/mao/libs/httplib"
	"gitee.com/go-mao/mao/libs/try"
	"gitee.com/go-mao/mao/libs/utils"
	"github.com/gin-gonic/gin"
)

type Client struct {
	domain          string
	appId           string
	appSecret       string
	checkResultHead bool
	checkResultCode func(code int64, res map[string]interface{})
	heads           map[string]string
	noticeTitle     string
	openProxyMode   bool
	timeout         time.Duration
}

func New(domain, appId, appSecret string) *Client {
	object := new(Client)
	object.domain = domain
	object.appId = appId
	object.appSecret = appSecret
	object.noticeTitle = "对接方"
	object.heads = make(map[string]string)
	object.timeout = 10
	return object
}

// 设置错误提示标题
func (this *Client) SetNoticeTitle(title string) {
	this.noticeTitle = title
}

// 设置为代理模式，不自动验证code
func (this *Client) SetProxyMode(b bool) {
	this.openProxyMode = b
}

// 设置验证header头
func (this *Client) SetCheckResultHead(b bool) {
	this.checkResultHead = b
}

func (this *Client) SetCheckResultCode(fn func(int64, map[string]interface{})) {
	this.checkResultCode = fn
}

// 生成返回header头
func (this *Client) MakeResponseHead(c *gin.Context) {
	reqToken := c.GetHeader("AppToken")
	timestamp := time.Now().Unix()
	c.Header("AppToken", utils.Sha1(this.appId, this.appSecret, c.Request.RequestURI, timestamp, reqToken))
	c.Header("AppTimestamp", fmt.Sprint(timestamp))
}

// 验证数据
func CheckToken(c *gin.Context, getSecret func(appId string) (secret string, err error)) error {
	var appId = c.GetHeader("AppId")
	var appToken = c.GetHeader("AppToken")
	var appTimestamp, _ = strconv.ParseInt(c.GetHeader("AppTimestamp"), 10, 64)
	if strings.TrimSpace(appId) == "" || strings.TrimSpace(appToken) == "" {
		return errors.New("请求参数异常")
	}
	secret, err := getSecret(appId)
	if err != nil {
		return err
	}
	if appToken != utils.Sha1(appId, secret, c.Request.RequestURI, appTimestamp) {
		return errors.New("Token验证失败")
	}
	timestamp := time.Now().Unix()
	if math.Abs(float64(timestamp-appTimestamp)) > 300 {
		return errors.New("Token已过期")
	}
	return nil
}

func (this *Client) Get(url string, resPtr interface{}) {
	this.handleRequest(http.MethodGet, url, nil, resPtr)
}

func (this *Client) Post(url string, data interface{}, resPtr interface{}) {
	this.handleRequest(http.MethodPost, url, data, resPtr)
}

func (this *Client) Put(url string, data interface{}, resPtr interface{}) {
	this.handleRequest(http.MethodPut, url, data, resPtr)
}

func (this *Client) Delete(url string, data interface{}, resPtr interface{}) {
	this.handleRequest(http.MethodDelete, url, data, resPtr)
}

func (this *Client) SetHead(key string, val interface{}) {
	this.heads[key] = fmt.Sprint(val)
}

// 超时时间，单位秒
func (this *Client) SetTimeout(t time.Duration) {
	this.timeout = t
}
func (this *Client) handleRequest(methods string, url string, data interface{}, resPtr interface{}) {
	var request *httplib.HttpRequest
	switch methods {
	case http.MethodPost:
		request = httplib.Post(this.domain + url)
		request.Body(data)
	case http.MethodPut:
		request = httplib.Put(this.domain + url)
		request.Body(data)
	case http.MethodDelete:
		request = httplib.Delete(this.domain + url)
		request.Body(data)
	default:
		request = httplib.Get(this.domain + url)
	}
	reqTimestamp := time.Now().Unix()
	reqToken := utils.Sha1(this.appId, this.appSecret, url, reqTimestamp)
	request.Header("AppId", this.appId)
	request.Header("AppToken", reqToken)
	request.Header("AppTimestamp", fmt.Sprint(reqTimestamp))
	request.SetTimeout(time.Second*this.timeout, time.Second*this.timeout)
	for k, v := range this.heads {
		request.Header(k, v)
	}
	if this.checkResultHead {
		rsp, err := request.Response()
		if err != nil {
			try.Throw(0, "接口响应异常", err.Error())
			return
		}
		//验证服务器端的签名
		resToken := rsp.Header.Get("AppToken")
		resTimestamp, _ := strconv.ParseInt(rsp.Header.Get("AppTimestamp"), 10, 64)
		if resToken != utils.Sha1(this.appId, this.appSecret, url, resTimestamp, reqToken) {
			try.Throw(0, this.noticeTitle, "返回签名无效")
			return
		}
		if resTimestamp-reqTimestamp > 300 || reqTimestamp-resTimestamp > 300 {
			try.Throw(0, this.noticeTitle, "返回签名过期")
			return
		}
	}
	var res = make(map[string]interface{})
	byteData, err := request.Bytes()
	if err != nil {
		try.Throw(0, this.noticeTitle, "接口异常", err.Error(), "返回结果为", string(byteData))
	}
	if !this.openProxyMode {
		//去除bom头
		byteData = bytes.TrimPrefix(byteData, []byte{239, 187, 191})
		err = json.Unmarshal(byteData, &res)
		if err != nil {
			try.Throw(0, this.noticeTitle, "返回数据错误", err.Error(), "返回结果为", string(byteData))
		}
		code, _ := binding.ToInt64(res["code"])
		switch code {
		case 100:
		//pass
		default:
			if _, ok := res["msg"]; !ok {
				res["msg"] = this.noticeTitle + "返回未知错误"
			}
			if this.checkResultCode != nil {
				this.checkResultCode(code, res)
			} else {
				try.Throw(0, this.noticeTitle, res["msg"].(string))
			}
		}
	}
	err = json.Unmarshal(byteData, resPtr)
	if err != nil {
		try.Throw(0, this.noticeTitle, "数据序列化失败", err.Error(), "返回结果为", string(byteData))
	}
}
